#include "sal.h"
#include "ctc_error.h"
#include "ctc_debug.h"
#include "ctc_interrupt.h"
#include "ctc_warmboot.h"
#include "drv_api.h"
#include "usw/include/drv_common.h"
#include "sys_usw_common.h"
#include "sys_usw_mac.h"
#include "sys_usw_eunit.h"
#include "sys_tmm_mcu.h"
#include "sys_tmm_mcu_fw.inc"
#include "sys_tmm_datapath.h"

extern uint32
sys_tmm_serdes_get_fw_length(void);

extern uint32 g_tmm_serdes_fw[];

int32
sys_tmm_mcu_read_shared_memory(uint8 lchip, uint8 mcu_id, uint32 addr, uint32 mask, uint32 *data)
{
    uint32 base_addr = 0;
    uint32 raw_data = 0;
    uint32 mask_op = ~mask;
    uint8 bit = 0;
    
    CTC_PTR_VALID_CHECK(data);
    
    switch(mcu_id)
    {
        case 0:
            base_addr = SYS_TMM_MCU_RAM_MEM_ADDR_0 + SYS_TMM_MCU_SHARED_MEM_OFFSET_ADDR;
            break;
        case 1:
            base_addr = SYS_TMM_MCU_RAM_MEM_ADDR_1 + SYS_TMM_MCU_SHARED_MEM_OFFSET_ADDR;
            break;
        case 2:
            base_addr = SYS_TMM_MCU_RAM_MEM_ADDR_2 + SYS_TMM_MCU_SHARED_MEM_OFFSET_ADDR;
            break;
        case 3:
            base_addr = SYS_TMM_MCU_RAM_MEM_ADDR_3 + SYS_TMM_MCU_SHARED_MEM_OFFSET_ADDR;
            break;
        case 4:
            base_addr = SYS_TMM_MCU_RAM_MEM_ADDR_4 + SYS_TMM_MCU_SHARED_MEM_OFFSET_ADDR;
            break;
        case 5:
            base_addr = SYS_TMM_MCU_RAM_MEM_ADDR_5 + SYS_TMM_MCU_SHARED_MEM_OFFSET_ADDR;
            break;
        case 6:

            base_addr = SYS_TMM_MCU_RAM_MEM_ADDR_6 + SYS_TMM_MCU_SHARED_MEM_OFFSET_ADDR;
            break;
        default:
            return CTC_E_INVALID_PARAM;
            
    }

    addr += base_addr; 

    CTC_ERROR_RETURN(drv_usw_chip_read(lchip, addr, &raw_data));

    raw_data &= mask_op;
    for(bit = 0; 0 == ((mask_op >> bit) & 0x1); bit++);
    *data = raw_data >> bit;

    return CTC_E_NONE;
}

int32
sys_tmm_mcu_read_shared_memory_u8(uint8 lchip, uint8 mcu_id, uint32 addr, uint8 *data)
{
    uint32 fix_addr = addr - (addr % 4);
    uint32 val32 = 0;

    CTC_ERROR_RETURN(sys_tmm_mcu_read_shared_memory(lchip, mcu_id, fix_addr , 0x0, &val32));

    *data = (val32 >> (8*(addr % 4))) & 0xff;

    return CTC_E_NONE;
}

int32
sys_tmm_mcu_write_shared_memory(uint8 lchip, uint8 mcu_id, uint32 addr, uint32 mask, uint32 data)
{
    uint32 base_addr = 0;
    uint32 raw_data = 0;
    uint32 mask_op = ~mask;
    uint8 bit = 0;
    
    switch(mcu_id)
    {
        case 0:
            base_addr = SYS_TMM_MCU_RAM_MEM_ADDR_0 + SYS_TMM_MCU_SHARED_MEM_OFFSET_ADDR;
            break;
        case 1:
            base_addr = SYS_TMM_MCU_RAM_MEM_ADDR_1 + SYS_TMM_MCU_SHARED_MEM_OFFSET_ADDR;
            break;
        case 2:
            base_addr = SYS_TMM_MCU_RAM_MEM_ADDR_2 + SYS_TMM_MCU_SHARED_MEM_OFFSET_ADDR;
            break;
        case 3:
            base_addr = SYS_TMM_MCU_RAM_MEM_ADDR_3 + SYS_TMM_MCU_SHARED_MEM_OFFSET_ADDR;
            break;
        case 4:
            base_addr = SYS_TMM_MCU_RAM_MEM_ADDR_4 + SYS_TMM_MCU_SHARED_MEM_OFFSET_ADDR;
            break;
        case 5:
            base_addr = SYS_TMM_MCU_RAM_MEM_ADDR_5 + SYS_TMM_MCU_SHARED_MEM_OFFSET_ADDR;
            break;
        case 6:
            base_addr = SYS_TMM_MCU_RAM_MEM_ADDR_6 + SYS_TMM_MCU_SHARED_MEM_OFFSET_ADDR;
            break;
        default:
            return CTC_E_INVALID_PARAM;
    }

    addr += base_addr; 

    if(0x0 == mask)
    {
        CTC_ERROR_RETURN(drv_usw_chip_write(lchip, addr, data));
    }
    else
    {
        for(bit = 0; 0 == ((mask_op >> bit) & 0x1); bit++);
        
        data = data << bit;
        
        CTC_ERROR_RETURN(drv_usw_chip_read(lchip, addr, &raw_data));

        data |= (raw_data & mask);
        CTC_ERROR_RETURN(drv_usw_chip_write(lchip, addr, data));
    }

    return CTC_E_NONE;
}

int32
_sys_tmm_mcu_load_fw(uint8 lchip, uint8 mcu_id)
{
    uint32 offset = 0;
    uint32 index;
    uint32 n;
    uint32 len = sizeof(g_tmm_mcu_fw) / sizeof(uint32);
    uint32 cmd = DRV_IOW(McpuMem_t, DRV_ENTRY_FLAG);
    McpuMem_m m_mem = {{0}};

    SYS_CONDITION_RETURN(SYS_TMM_MCU_NUM <= mcu_id, CTC_E_INVALID_CONFIG);

    for(n = 0; n < len; n++)
    {
        index = DRV_INS(((uint32)mcu_id), offset);
        SetMcpuMem(V, data_f, &m_mem, g_tmm_mcu_fw[n]);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, index, cmd, &m_mem));
        offset++;
    }

    return CTC_E_NONE;
}

int32
sys_tmm_mcu_show_debug_info(uint8 lchip, uint8 _in_mcu_id)
{
    uint8 mcu_id                = 0;
    uint8 mcu_bmp               = 0x24; /*0b 0100 0100, MCU ID 2 & 5*/
    uint8 lane_id               = 0;
    uint8 phy_num               = 0;
    uint8 debug_addr_offset     = 0;
    uint8 enable                = 0;
    uint8 work_mode             = 0;
    uint8 pcs_chan_bmp          = 0;
    uint8 serdes_lane_bmp       = 0;
    uint8 cs_id                 = 0;
    uint8 adjust_cnt            = 0;
    uint8 buf_depth_max         = 0;
    uint8 buf_depth_min         = 0;
    uint8 expect_max            = 0;
    uint8 expect_min            = 0;
    uint8 val_u8                = 0;
    uint16 val_0xfd_old         = 0;
    uint16 val_0xfd_new         = 0;
    uint32 val32                = 0;
    uint32 cmd_status           = 0;
    char* cmd_status_str[SYS_MCU_FLEXE_CMD_STATUS_MAX] = {"Doing", "Done", "Time Out", "Fail"};  
    char* work_mode_str[] = {"NONE / RS528", "RS544"};

    for(mcu_id = 0; mcu_id < 7; mcu_id ++)
    {
        if(0 == ((mcu_bmp >> mcu_id) & 0x1))
        {
            continue;
        }

        SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "\n----------------------------------------- MCU %u info -----------------------------------------\n", mcu_id);
        CTC_ERROR_RETURN(sys_tmm_mcu_read_shared_memory(lchip,      mcu_id, GLB_INFO_SW_MODE, 0x0, &val32));
        SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    %-16s : %s\n", "MCU Mode", (val32 == FLEXE_ADJ_MODE ? "FLEXE_ADJ" : "NORMAL_MODE"));

        if(FLEXE_ADJ_MODE != val32)
        {
            continue;
        }

        CTC_ERROR_RETURN(sys_tmm_mcu_read_shared_memory(lchip,       mcu_id, GLB_INFO_FLEXE_CMD_STATUS, 0x0, &cmd_status));        
        CTC_ERROR_RETURN(sys_tmm_mcu_read_shared_memory(lchip,      mcu_id, GLB_INFO_DBG_ADDR_5, 0x0, &val32));
        enable = (val32 >> 0*8) & 0xf;
        cs_id = (val32 >> 4) & 0xf;
        work_mode = (val32 >> 1*8) & 0xff;
        pcs_chan_bmp = (val32 >> 2*8) & 0xff;
        serdes_lane_bmp = (val32 >> 3*8) & 0xff;

        if((work_mode >= 2) || (cmd_status >= SYS_MCU_FLEXE_CMD_STATUS_MAX))
        {
            SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    Read Info 0x%x is Invaild!\n", val32);
            continue;
        }

        if(0x0 == pcs_chan_bmp)
        {   
            SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    %-16s : %s\n", "CS ID", "-");
            SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    %-16s : %s\n", "PCS LANE BMP", "-");
            SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    %-16s : %s\n", "SerDes LANE BMP", "-");
            SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    %-16s : %s\n", "FEC Mode", "-");
            SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    %-16s : %s\n", "Adjust Enable", "-");
            SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    %-16s : %s\n", "Adjust Count", "-");
            SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    %-16s : %s\n", "Buf Depth Range", "-");
        }
        else
        {
            SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    %-16s : %d\n", "CS ID", cs_id);
            SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    %-16s : 0x%x\n", "PCS LANE BMP", pcs_chan_bmp);
            SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    %-16s : 0x%x\n", "SerDes LANE BMP", serdes_lane_bmp);
            SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    %-16s : %s\n", "FEC Mode", work_mode_str[work_mode]);
            SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    %-16s : %s\n", "Adjust Enable", ((enable == 1) ? "Yes" : ( enable ==  2 ? "No" : "-")));
            SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    %-16s : %s\n", "Adjust Result", cmd_status_str[cmd_status]);

            CTC_ERROR_RETURN(sys_tmm_mcu_read_shared_memory_u8(lchip,       mcu_id, GLB_INFO_DBG_ADDR_6 + 0, &expect_min));
            CTC_ERROR_RETURN(sys_tmm_mcu_read_shared_memory_u8(lchip,       mcu_id, GLB_INFO_DBG_ADDR_6 + 1, &expect_max));

            CTC_ERROR_RETURN(sys_tmm_mcu_read_shared_memory_u8(lchip,       mcu_id, GLB_INFO_DBG_ADDR_6 + 2, &val_u8));
            val_0xfd_old |= val_u8;
            CTC_ERROR_RETURN(sys_tmm_mcu_read_shared_memory_u8(lchip,       mcu_id, GLB_INFO_DBG_ADDR_6 + 3, &val_u8));
            val_0xfd_old |= (val_u8 << 8); 

            CTC_ERROR_RETURN(sys_tmm_mcu_read_shared_memory_u8(lchip,       mcu_id, GLB_INFO_DBG_ADDR_7 + 0, &val_u8));
            val_0xfd_new |= val_u8;
            CTC_ERROR_RETURN(sys_tmm_mcu_read_shared_memory_u8(lchip,       mcu_id, GLB_INFO_DBG_ADDR_7 + 1, &val_u8));
            val_0xfd_new |= (val_u8 << 8);

            SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    %-16s : 0x%x\n", "val_0xfd_old", val_0xfd_old);
            SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    %-16s : 0x%x\n", "val_0xfd_new", val_0xfd_new);

            phy_num = 0;

            for(lane_id = 0; lane_id < 8; lane_id ++)
            {
                if(0 == ((pcs_chan_bmp >> lane_id) & 0x1))
                {
                    continue;
                }

                debug_addr_offset = phy_num * 3;
                CTC_ERROR_RETURN(sys_tmm_mcu_read_shared_memory_u8(lchip,       mcu_id, GLB_INFO_DBG_ADDR_8 + (debug_addr_offset ++), &adjust_cnt));
                CTC_ERROR_RETURN(sys_tmm_mcu_read_shared_memory_u8(lchip,       mcu_id, GLB_INFO_DBG_ADDR_8 + (debug_addr_offset ++), &buf_depth_max));
                CTC_ERROR_RETURN(sys_tmm_mcu_read_shared_memory_u8(lchip,       mcu_id, GLB_INFO_DBG_ADDR_8 + (debug_addr_offset ++), &buf_depth_min));
                SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "\n    ------------ PCS Lane ID %d ------------\n", lane_id);
                SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    %-16s  : %d\n", "Adjust Count", adjust_cnt);
                SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    %-16s  : (%d, %d)\n", "Expect Range", expect_min, expect_max);
                SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "    %-16s  : [%d, %d]\n", "Buf Depth Range", buf_depth_min, buf_depth_max);

                phy_num ++;
            }
        }
    }

    return CTC_E_NONE;
}

int32 sys_tmm_mcu_reset_core(uint8 lchip, uint32 mcu_id, uint8 reset)
{
    uint32 index     = 0;
    uint32 cmd       = 0;
    uint32 value     = 0;
    uint32 tbl_info[7][3] = {
      /* tbl_id                 field_id                            ins */
        {CtcHsCtlReset_t,       CtcHsCtlReset_resetCoreMcu_f,       0},  /* MCU ID : 0, HSS 0 & 1*/
        {CtcHsCtlReset_t,       CtcHsCtlReset_resetCoreMcu_f,       1},  /* MCU ID : 1, HSS 2 & 3*/
        {CtcFlexeCrossReset_t,  CtcFlexeCrossReset_resetCoreMcu_f,  0},  /* MCU ID : 2, HSS 4 & 5*/
        {CtcHsCtlReset_t,       CtcHsCtlReset_resetCoreMcu_f,       2},  /* MCU ID : 3, HSS 6 & 7*/
        {CtcHsCtlReset_t,       CtcHsCtlReset_resetCoreMcu_f,       3},  /* MCU ID : 4, HSS 8 & 9*/
        {CtcFlexeCrossReset_t,  CtcFlexeCrossReset_resetCoreMcu_f,  1},  /* MCU ID : 5, HSS 10 & 11*/
        {CtcMiscCtlReset_t,     CtcMiscCtlReset_resetMcu_f,         0},  /* MCU ID : 6, CPUMAC*/
    };

    index = DRV_INS(tbl_info[mcu_id][2], 0);
    cmd = DRV_IOW(tbl_info[mcu_id][0], tbl_info[mcu_id][1]);
    value = reset ? 1 : 0;
    
    CTC_ERROR_RETURN(DRV_FIELD_IOCTL(lchip, index, cmd, &value));
    
    return CTC_E_NONE;
}

int32
sys_tmm_mcu_init(uint8 lchip, uint32 mcu_id, uint8 hss_iddq_flag[])
{
    uint8  item;
    uint32 idx;
    uint32 length = sys_tmm_serdes_get_fw_length();
    uint32 hss_id;
    uint32 hss_id_delta;
    uint32 value = 0;
    uint32 glb_info[13][2] = {
        {GLB_INFO_MCU_ID             , 0},
        {GLB_INFO_LOAD_OK_IND        , 0},
        {GLB_INFO_LOAD_ERR_IND       , 0},
        {GLB_INFO_LOAD_STATUS        , 0},
        {GLB_INFO_REG_ALL_DELAY_US   , 2},
        {GLB_INFO_STEP0_DELAY_US     , 0},
        {GLB_INFO_FLEXE_CMD_CODE     , 0},
        {GLB_INFO_FLEXE_CMD_STATUS   , 0},
        {GLB_INFO_FLEXE_CMD_RESULT   , 0},
        {GLB_INFO_SW_MODE            , 0},
        {GLB_INFO_FLEXE_ADJUST_SWITCH, 1},
        {GLB_INFO_TMM_LOAD_FW_SWITCH , 1},
        {GLB_INFO_OCTAL_SERDES_EN    , 0},
    };
    uint32 serdes_fw_anchor[SYS_TMM_MCU_NUM] = {
        SYS_TMM_MCU_SERDES_FW_ANCHOR_0,
        SYS_TMM_MCU_SERDES_FW_ANCHOR_1,
        SYS_TMM_MCU_SERDES_FW_ANCHOR_2,
        SYS_TMM_MCU_SERDES_FW_ANCHOR_3,
        SYS_TMM_MCU_SERDES_FW_ANCHOR_4,
        SYS_TMM_MCU_SERDES_FW_ANCHOR_5,
        SYS_TMM_MCU_SERDES_FW_ANCHOR_6,
    };

    SYS_CONDITION_RETURN(SYS_TMM_MCU_NUM <= mcu_id, CTC_E_INVALID_PARAM);

    /*1. write serdes FW into MCU memory*/
    for(idx = 0; idx < length; idx++)
    {
        CTC_ERROR_RETURN(drv_usw_chip_write(lchip, (serdes_fw_anchor[mcu_id]+idx*4),  g_tmm_serdes_fw[idx]));
    }

    /*2. load mcu firmware */
    CTC_ERROR_RETURN(_sys_tmm_mcu_load_fw(lchip, mcu_id));

    /*3. init shared memory */
    /*GLB_INFO_MCU_ID*/
    CTC_ERROR_RETURN(sys_tmm_mcu_write_shared_memory(lchip, mcu_id, glb_info[0][0], 0x0, mcu_id));
    /*GLB_INFO_OCTAL_SERDES_EN*/
    for(hss_id_delta = 0; hss_id_delta < 2; hss_id_delta++)
    {
        hss_id = mcu_id * 2 + hss_id_delta;
        SYS_CONDITION_CONTINUE((SYS_TMM_CPUMAC_HSS_ID < hss_id) || (FALSE == hss_iddq_flag[hss_id]));
        value |= (0x01 << hss_id_delta);
    }
    CTC_ERROR_RETURN(sys_tmm_mcu_write_shared_memory(lchip, mcu_id, glb_info[12][0], 0x0, value));
    /*other options*/
    for(item = 1; item < 12; item++)
    {
        CTC_ERROR_RETURN(sys_tmm_mcu_write_shared_memory(lchip, mcu_id, glb_info[item][0], 0x0, glb_info[item][1]));
    }

    return CTC_E_NONE;
}

/*run serdes loading program for all lanes*/
int32
sys_tmm_mcu_serdes_loading(uint8 lchip, uint8 hss_iddq_flag[])
{
    uint32 mcu_id      = 0;
    uint32 times       = 1000;
    uint8  all_ok_flag = FALSE;
    uint32 value       = 0;
    uint32 load_ok_addr[SYS_TMM_MCU_NUM]  = {0x6241fc04, 0x6249fc04, 0x6101fc04, 0x6a41fc04, 0x6a49fc04, 0x6901fc04, 0x5549fc04};
    uint32 load_err_addr[SYS_TMM_MCU_NUM] = {0x6241fc08, 0x6249fc08, 0x6101fc08, 0x6a41fc08, 0x6a49fc08, 0x6901fc08, 0x5549fc08};
    uint8  mcu_is_valid[SYS_TMM_MCU_NUM] = {TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE};

    if(DRV_CHIP_SUB_TYPE_4 == DRV_CHIP_SUB_TYPE(lchip))
    {
        mcu_is_valid[0] = FALSE;
        mcu_is_valid[1] = FALSE;
        mcu_is_valid[3] = FALSE;
        mcu_is_valid[4] = FALSE;
    }

    /*uint32 cmd;
    value = 0x10;
    UartIntfSel_m uart_sel;
    DRV_IOW_FIELD(lchip, UartIntfSel_t, UartIntfSel_cfgUartSel_f, &value, &uart_sel);
    cmd = DRV_IOW(UartIntfSel_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &uart_sel));*/

    SYS_CONDITION_RETURN((p_drv_master[lchip]->wb_status == DRV_WB_STATUS_RELOADING), CTC_E_NONE);

    /*0. mcu program setup*/
    for(mcu_id = 0; mcu_id < SYS_TMM_MCU_NUM; mcu_id ++)
    {
        SYS_CONDITION_CONTINUE(!mcu_is_valid[mcu_id]);
        CTC_ERROR_RETURN(sys_tmm_mcu_init(lchip, mcu_id, hss_iddq_flag));
    }

    /*1. release mcu reset to run loading*/
    for(mcu_id = 0; mcu_id < SYS_TMM_MCU_NUM; mcu_id ++)
    {
        SYS_CONDITION_CONTINUE(!mcu_is_valid[mcu_id]);
        CTC_ERROR_RETURN(sys_tmm_mcu_reset_core(lchip, mcu_id, 0));
    }

    /*2. check load ok ind*/
    while(--times)
    {
        for(mcu_id = 0; mcu_id < SYS_TMM_MCU_NUM; mcu_id++)
        {
            all_ok_flag = TRUE;
            SYS_CONDITION_CONTINUE(!mcu_is_valid[mcu_id]);
            (void)drv_usw_chip_read(lchip, load_ok_addr[mcu_id], &value);
            if(0 == value)
            {
                all_ok_flag = FALSE;
                break;
            }
        }
        if(all_ok_flag)
        {
            break;
        }
        sal_task_sleep(10);
    }
    if(0 == times)
    {
        SYS_DATAPATH_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "\n...MCU %u FW LOAD TIMEOUT!\n", mcu_id);
        return CTC_E_HW_TIME_OUT;
    }

    /*3. check load error ind*/
    for(mcu_id = 0; mcu_id < SYS_TMM_MCU_NUM; mcu_id++)
    {
        SYS_CONDITION_CONTINUE(!mcu_is_valid[mcu_id]);
        (void)drv_usw_chip_read(lchip, load_err_addr[mcu_id], &value);
        if(1 == value)
        {
            SYS_DATAPATH_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "\n...MCU %u FW LOAD ERROR!\n", mcu_id);
            return CTC_E_INIT_FAIL;
        }
    }

    return CTC_E_NONE;
}



